import type { ComputedRef, Ref } from 'vue';
import type {
  FormProps,
  FormSchemaInner as FormSchema,
  FormActionType,
} from '../types/form';
import type { NamePath } from 'ant-design-vue/lib/form/interface';
import { unref, toRaw, nextTick } from 'vue';
import {
  isArray,
  isFunction,
  isObject,
  isString,
  isDef,
  isNil,
  isEmpty,
  isBoolean,
} from '@/utils/is';
import { deepMerge } from '@/utils';
import {
  dateItemType,
  handleInputNumberValue,
  defaultValueComponents,
} from '../helper';
import { dateUtil } from '@/utils/dateUtil';
import { cloneDeep, set, uniqBy, get, has } from 'lodash-es';
import { error } from '@/utils/log';

interface UseFormActionContext {
  emit: EmitType;
  getProps: ComputedRef<FormProps>;
  getSchema: ComputedRef<FormSchema[]>;
  formModel: Recordable;
  defaultValueRef: Ref<Recordable>;
  formElRef: Ref<FormActionType>;
  schemaRef: Ref<FormSchema[]>;
  handleFormValues: Fn;
}

function tryConstructArray(
  field: string,
  values: Recordable = {},
): any[] | undefined {
  const pattern = /^\[(.+)\]$/;
  if (pattern.test(field)) {
    const match = field.match(pattern);
    if (match && match[1]) {
      const keys = match[1].split(',');
      if (!keys.length) {
        return undefined;
      }

      const result = [];
      keys.forEach((k, index) => {
        set(result, index, values[k.trim()]);
      });

      return result.length ? result : undefined;
    }
  }
}

function tryConstructObject(
  field: string,
  values: Recordable = {},
): Recordable | undefined {
  const pattern = /^\{(.+)\}$/;
  if (pattern.test(field)) {
    const match = field.match(pattern);
    if (match && match[1]) {
      const keys = match[1].split(',');
      if (!keys.length) {
        return;
      }

      const result = {};
      keys.forEach((k) => {
        set(result, k.trim(), values[k.trim()]);
      });

      return Object.values(result).filter(Boolean).length ? result : undefined;
    }
  }
}

export function useFormEvents({
  emit,
  getProps,
  formModel,
  getSchema,
  defaultValueRef,
  formElRef,
  schemaRef,
  handleFormValues,
}: UseFormActionContext) {
  async function resetFields(): Promise<void> {
    const { resetFunc, submitOnReset } = unref(getProps);
    resetFunc && isFunction(resetFunc) && (await resetFunc());

    const formEl = unref(formElRef);
    if (!formEl) return;

    Object.keys(formModel).forEach((key) => {
      const schema = unref(getSchema).find((item) => item.field === key);
      const isInput =
        schema?.component && defaultValueComponents.includes(schema.component);
      const defaultValue = cloneDeep(defaultValueRef.value[key]);
      formModel[key] = isInput ? defaultValue || '' : defaultValue;
    });
    nextTick(() => clearValidate());

    emit('reset', toRaw(formModel));
    submitOnReset && handleSubmit();
  }
  /**
   * 上下级schema压扁成一级
   * @param nSchemas 源
   * @param oSchemas 目标
   */
  function getSchemas(nSchemas, oSchemas) {
    oSchemas.forEach((os) => {
      nSchemas.push(cloneDeep(os));
      if (os.children) getSchemas(nSchemas, os.children);
    });
  }

  /**
   * @description: Set form value
   */
  async function setFieldsValue(values: Recordable): Promise<void> {
    const schemas: FormSchema[] = [];
    getSchemas(schemas, unref(getSchema));
    const fields = schemas.map((item) => item.field).filter(Boolean);

    // key 支持 a.b.c 的嵌套写法
    const delimiter = '.';
    const nestKeyArray = fields.filter(
      (item) => String(item).indexOf(delimiter) >= 0,
    );
    const validKeys: string[] = [];
    fields.forEach((key) => {
      const schema = schemas.find((item) => item.field === key);
      let value = get(values, key);
      // 如果 values {a: null} 返回false！！
      // 如果使用setFieldsValue({a:null})不生效！！
      const hasKey = has(values, key);

      value = handleInputNumberValue(schema?.component, value);
      const { componentProps } = schema || {};
      let _props = componentProps as any;
      if (typeof componentProps === 'function') {
        _props = _props({ formModel: unref(formModel) });
      }

      const constructValue =
        tryConstructArray(key, values) || tryConstructObject(key, values);

      // 0| '' is allow
      if (hasKey || !!constructValue) {
        const fieldValue = constructValue || value;
        // time type
        if (itemIsDateType(key)) {
          if (Array.isArray(fieldValue)) {
            const arr: any[] = [];
            for (const ele of fieldValue) {
              arr.push(ele ? dateUtil(ele) : null);
            }
            unref(formModel)[key] = arr;
          } else {
            unref(formModel)[key] = fieldValue
              ? _props?.valueFormat
                ? fieldValue
                : dateUtil(fieldValue)
              : null;
          }
        } else {
          unref(formModel)[key] = fieldValue;
        }
        // if (_props?.onChange) {
        //   _props?.onChange(fieldValue);
        // }
        validKeys.push(key);
      } else {
        nestKeyArray.forEach((nestKey: string) => {
          try {
            const value = nestKey
              .split('.')
              .reduce((out, item) => out[item], values);
            if (isDef(value)) {
              unref(formModel)[nestKey] = unref(value);
              validKeys.push(nestKey);
            }
          } catch (e) {
            // key not exist
            if (isDef(defaultValueRef.value[nestKey])) {
              unref(formModel)[nestKey] = cloneDeep(
                unref(defaultValueRef.value[nestKey]),
              );
            }
          }
        });
      }
    });
    validateFields(validKeys).catch((_) => {});
  }

  /**
   * @description: Delete based on field name
   */
  async function removeSchemaByField(fields: string | string[]): Promise<void> {
    const schemaList: FormSchema[] = cloneDeep(unref(getSchema));
    if (!fields) {
      return;
    }

    let fieldList: string[] = isString(fields) ? [fields] : fields;
    if (isString(fields)) {
      fieldList = [fields];
    }
    for (const field of fieldList) {
      _removeSchemaByField(field, schemaList);
    }
    schemaRef.value = schemaList;
  }

  /**
   * @description: Delete based on field name
   */
  function _removeSchemaByField(field: string, schemaList: FormSchema[]): void {
    if (isString(field)) {
      const index = schemaList.findIndex((schema) => schema.field === field);
      if (index !== -1) {
        delete formModel[field];
        schemaList.splice(index, 1);
      }
    }
  }

  /**
   * @description: Insert after a certain field, if not insert the last
   */
  async function appendSchemaByField(
    schema: FormSchema | FormSchema[],
    prefixField?: string,
    first = false,
  ) {
    const schemaList: FormSchema[] = cloneDeep(unref(getSchema));
    const index = schemaList.findIndex(
      (schema) => schema.field === prefixField,
    );
    const _schemaList = isObject(schema)
      ? [schema as FormSchema]
      : (schema as FormSchema[]);
    if (!prefixField || index === -1 || first) {
      first
        ? schemaList.unshift(..._schemaList)
        : schemaList.push(..._schemaList);
      schemaRef.value = schemaList;
      _setDefaultValue(schema);
      return;
    }
    if (index !== -1) {
      schemaList.splice(index + 1, 0, ..._schemaList);
    }
    _setDefaultValue(schema);

    schemaRef.value = schemaList;
  }

  async function resetSchema(
    data: Partial<FormSchema> | Partial<FormSchema>[],
  ) {
    let updateData: Partial<FormSchema>[] = [];
    if (isObject(data)) {
      updateData.push(data as FormSchema);
    }
    if (isArray(data)) {
      updateData = [...data];
    }

    const hasField = updateData.every(
      (item) =>
        item.component === 'Divider' ||
        item.component === 'CollapseContainer' ||
        (Reflect.has(item, 'field') && item.field),
    );

    if (!hasField) {
      error(
        'All children of the form Schema array that need to be updated must contain the `field` field',
      );
      return;
    }
    schemaRef.value = updateData as FormSchema[];
  }

  /**
   * 更新表单 Schema 数据，支持更新嵌套数组中的表单项
   * @param data 待更新的部分表单 Schema 或部分表单 Schema 数组
   */
  async function updateSchema(
    data: Partial<FormSchema> | Partial<FormSchema>[],
  ) {
    // 初始化更新数据数组
    const updateData: Partial<FormSchema>[] = isArray(data)
      ? [...data]
      : [data];

    // 检查更新数据是否包含必需的字段
    const hasField = updateData.every(
      (item) =>
        item.component === 'Divider' ||
        item.component === 'CollapseContainer' ||
        (Reflect.has(item, 'field') && item.field),
    );

    // 如果更新数据不合法，输出错误信息并返回
    if (!hasField) {
      error(
        'All children of the form Schema array that need to be updated must contain the `field` field',
      );
      return;
    }

    // 构建新的表单 Schema 数组
    const schema: FormSchema[] = unref(getSchema).map((val) => {
      let _val = val;

      // 如果当前项是 'CollapseContainer' 组件且有子项，则递归更新子项
      if (
        val.component === 'CollapseContainer' &&
        Array.isArray(val.children)
      ) {
        _val.children = updateNestedItems(val.children, updateData);
      } else {
        // 查找并处理更新后的数据
        const updatedItem = updateData.find((item) => item.field === val.field);
        if (updatedItem) {
          _val = updatedItem.replaceSchema
            ? updatedItem
            : deepMerge(val, updatedItem);
        }
      }

      return _val;
    });

    // 设置默认值
    _setDefaultValue(schema);

    // 更新表单 Schema 并去重
    schemaRef.value = uniqBy(schema, 'field');
  }

  /**
   * 更新嵌套数组中的表单项
   * @param items 原始的嵌套表单项数组
   * @param updateData 待更新的部分表单项数组
   * @returns 更新后的嵌套表单项数组
   */
  function updateNestedItems(
    items: FormSchema[],
    updateData: Partial<FormSchema>[],
  ): FormSchema[] {
    // 递归函数，用于遍历表单项数组并更新对象
    function recursiveUpdate(arr: FormSchema[]): FormSchema[] {
      return arr.map((item) => {
        // 如果当前项是 'CollapseContainer' 组件且有子项，则递归更新子项
        if (
          item.component === 'CollapseContainer' &&
          Array.isArray(item.children)
        ) {
          item.children = recursiveUpdate(item.children);
        }
        // 查找并更新当前项
        const updatedItem = updateData.find(
          (newItem) => newItem.field === item.field,
        );
        if (updatedItem) {
          // 如果找到了匹配的更新数据，根据配置进行替换或深度合并
          return updatedItem.replaceSchema
            ? { ...updatedItem }
            : deepMerge(item, updatedItem);
        }
        // 如果没有找到匹配的更新数据，则保持当前项不变
        return item;
      });
    }

    // 调用递归函数并返回更新后的嵌套表单项数组
    return recursiveUpdate(items);
  }

  /**
   * 设置表单项的默认值，支持嵌套结构
   * @param data 待设置默认值的表单 Schema 或表单 Schema 数组
   */
  function _setDefaultValue(data: FormSchema | FormSchema[]) {
    // 将传入的数据转换为数组形式
    const schemas: FormSchema[] = isArray(data) ? data : [data];
    // 存储字段及其默认值的对象
    const defaultValues: Record<string, any> = {};

    /**
     * 递归函数，用于设置默认值
     * @param item 当前处理的表单项
     */
    function setDefaultValue(item: FormSchema) {
      const currentFieldsValue = getFieldsValue();
      // 如果当前项不是 'Divider' 并且包含 'field' 字段以及默认值，则设置默认值
      if (
        item.component !== 'Divider' &&
        Reflect.has(item, 'field') &&
        item.field &&
        !isNil(item.defaultValue) &&
        (!(item.field in currentFieldsValue) ||
          isNil(currentFieldsValue[item.field]) ||
          (isEmpty(currentFieldsValue[item.field]) &&
            !isBoolean(currentFieldsValue[item.field])))
      ) {
        defaultValues[item.field] = item.defaultValue;
      }
      // 如果当前项是 'CollapseContainer' 或包含 'children' 字段，则递归处理其 children
      if (
        (item.component === 'CollapseContainer' ||
          Reflect.has(item, 'children')) &&
        Array.isArray(item.children)
      ) {
        item.children.forEach(setDefaultValue);
      }
      // 如果当前项包含 'children' 字段，且不是 'CollapseContainer'，则处理其 children 的默认值
      else if (Reflect.has(item, 'children')) {
        item.children.forEach((child) => {
          if (
            child.component !== 'Divider' &&
            Reflect.has(child, 'field') &&
            child.field &&
            !isNil(child.defaultValue) &&
            (!(child.field in currentFieldsValue) ||
              isNil(currentFieldsValue[child.field]) ||
              (isEmpty(currentFieldsValue[child.field]) &&
                !isBoolean(currentFieldsValue[child.field])))
          ) {
            // 设置正确的路径
            const path = `${item.field}.${child.field}`;
            defaultValues[path] = child.defaultValue;
          }
        });
      }
    }

    // 遍历表单项数组并设置默认值
    schemas.forEach(setDefaultValue);

    // 设置默认值
    setFieldsValue(defaultValues);
  }

  function getFieldsValue(): Recordable {
    const formEl = unref(formElRef);
    if (!formEl) return {};
    return handleFormValues(toRaw(unref(formModel)));
  }

  /**
   * @description: Is it time
   */
  function itemIsDateType(key: string) {
    return unref(getSchema).some((item) => {
      return item.field === key ? dateItemType.includes(item.component) : false;
    });
  }

  async function validateFields(nameList?: NamePath[] | undefined) {
    return unref(formElRef)?.validateFields(nameList);
  }

  async function validate(nameList?: NamePath[] | undefined) {
    return await unref(formElRef)?.validate(nameList);
  }

  async function clearValidate(name?: string | string[]) {
    await unref(formElRef)?.clearValidate(name);
  }

  async function scrollToField(
    name: NamePath,
    options?: ScrollOptions | undefined,
  ) {
    await unref(formElRef)?.scrollToField(name, options);
  }

  /**
   * @description: Form submission
   */
  async function handleSubmit(e?: Event): Promise<void> {
    e && e.preventDefault();
    const { submitFunc } = unref(getProps);
    if (submitFunc && isFunction(submitFunc)) {
      await submitFunc();
      return;
    }
    const formEl = unref(formElRef);
    if (!formEl) return;
    try {
      const values = await validate();
      const res = handleFormValues(values);
      emit('submit', res);
    } catch (error: any) {
      if (error?.outOfDate === false && error?.errorFields) {
        return;
      }
      throw new Error(error);
    }
  }

  return {
    handleSubmit,
    clearValidate,
    validate,
    validateFields,
    getFieldsValue,
    updateSchema,
    resetSchema,
    appendSchemaByField,
    removeSchemaByField,
    resetFields,
    setFieldsValue,
    scrollToField,
  };
}
